﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Text.RegularExpressions;

namespace pnh
{
    class Scan
    {
        private class PlayerInfo
        {
            public string _strGamertag;
            public int _iID;
            public int _iGamerscore;
            public int _iScanImage;
            public int _iFloatingGamerscore;
        }

        public delegate void consoleWriteLineDelegate(string str);

        private consoleWriteLineDelegate _delConsole;
        private pnhBrowserControl.pnhBowserControl _browser;
        const string _urlCompareActivity = "https://live.xbox.com/fr-FR/Activity?compareTo=";

        public Scan(consoleWriteLineDelegate del, pnhBrowserControl.pnhBowserControl b)
        {
            _delConsole = del;
            _browser = b;
        }

        private void consoleWriteLine(string str)
        {
            _delConsole.Invoke(str);
        }

        /// <summary>
        /// Do an incremental scan of all the players
        /// </summary>
        public void scanIncAllPlayer()
        {
            //get list of players
            string playersList = DBInterface.getPlayersList();
            string[] arrPlayersList = playersList.Split(',');

            //for each player
            foreach (string player in arrPlayersList)
            {
                string[] arrPlayer = player.Split(';');

                //get player data
                PlayerInfo pi = new PlayerInfo();
                pi._strGamertag = arrPlayer[0];
                pi._iID = int.Parse(arrPlayer[1]);
                pi._iGamerscore = int.Parse(arrPlayer[2]);
                pi._iScanImage = int.Parse(arrPlayer[4]);

                consoleWriteLine("scan " + pi._strGamertag + ", ID=" + pi._iID.ToString());
                
                //scan game player
                if (!scanIncPlayer(pi))
                    return;
            }
        }

        public void connect()
        {
            string url = "https://login.live.com/login.srf?wa=wsignin1.0&rpsnv=11&ct=1335528462&rver=6.0.5286.0&wp=MBI&wreply=https:%2F%2Flive.xbox.com:443%2Fxweb%2Flive%2Fpassport%2FsetCookies.ashx%3Frru%3Dhttps%253a%252f%252flive.xbox.com%252fen-US%252fAccount%252fSignin%253freturnUrl%253dhttp%25253a%25252f%25252fwww.xbox.com%25252fen-US%25252f&lc=1033&id=66262&cbcxt=0";
            string html = _browser.navigate(url);
            if (html.Contains("sign in"))
                consoleWriteLine("Demande de connection");
            else
                consoleWriteLine("Deja connecter");

            
        }

        /// <summary>
        /// Scan entierly a player
        /// </summary>
        /// <param name="gamertag"></param>
        public void scanPlayer(string gamertag)
        {
            //get list of players
            string playersList = DBInterface.getPlayersList();
            string[] arrPlayersList = playersList.Split(',');

            //for each player
            foreach (string player in arrPlayersList)
            {
                string[] arrPlayer = player.Split(';');

                if (arrPlayer[0] != gamertag)
                    continue;

                //get player data
                PlayerInfo pi = new PlayerInfo();
                pi._strGamertag = arrPlayer[0];
                pi._iID = int.Parse(arrPlayer[1]);
                pi._iGamerscore = int.Parse(arrPlayer[2]);
                pi._iScanImage = int.Parse(arrPlayer[4]);

                consoleWriteLine("scan " + pi._strGamertag + ", ID=" + pi._iID.ToString());

                //scan game player
                scanPlayer(pi);
            }
        }

        #region Tools

        /// <summary>
        /// Non incremental scan of a player
        /// </summary>
        /// <param name="player"></param>
        private void scanPlayer(PlayerInfo player)
        {
            //call the activity page
            _browser.getWebPageAchievement("about:blank");
            string url = _urlCompareActivity + player._strGamertag.Replace(" ", "%20");
            string html = _browser.getWebPageActivity(url, player._strGamertag);

            //get the gamerscore
            //gamerscore => resMatch.Groups[1].Value
            string pattern = "<LI class=header>.*?<DIV class=stat>([0-9]*)?</DIV>";
            Match resMatch = Regex.Match(html, pattern, RegexOptions.Singleline);

            while (resMatch.Groups.Count != 2)
            {
                html = _browser.getWebPageActivity(url, player._strGamertag);
                resMatch = Regex.Match(html, pattern, RegexOptions.Singleline);
            }

            //update gamer picture
            if (player._iScanImage == 1)
            {
                consoleWriteLine("-Update Gamer Picture");
                updateGamerPicture(html, player);
            }

            //check if gamerscore changed
            int currentGamerscore = int.Parse(resMatch.Groups[1].Value);
            consoleWriteLine("-XBOXLIVE.com Gamerscore=" + currentGamerscore.ToString());
            consoleWriteLine("-ETERNAL-APOCALYPSE.fr Gamerscore=" + player._iGamerscore.ToString());
            //if (currentGamerscore == player._iGamerscore)
            //    return;

            //update gamerscore
            //player._iFloatingGamerscore = player._iGamerscore;

            //extract games
            pattern = "<LI class=\"game.*?</LI>";
            MatchCollection matchCol = Regex.Matches(html, pattern, RegexOptions.Singleline);

            //for each game
            for (int i = 0; i < matchCol.Count; ++i)
            {
                Match m = matchCol[i];
                //if (player._iFloatingGamerscore >= currentGamerscore)
                //{
                    //update gamerscore
                //    DBInterface.updatePlayerGamerscore(player._iID, currentGamerscore);
                //    return;
                //}

                //extract game info
                //1=>msid
                //2=>box
                //3=>name
                //4=>achievements unlocked count
                //5=>achievements count
                pattern = "<LI class=\"game ([0-9]*)?\".*?src=\"(.*)?\"></A>.*?<A class=gameTitle.*?>(.*)?</A></H3>.*?<DIV class=achievement><SPAN>([0-9]*)</SPAN>/<SPAN>([0-9]*)</SPAN>";
                Match acheMatch = Regex.Match(m.Groups[0].Value, pattern, RegexOptions.Singleline);

                //no data then no achievement so no scan
                if (acheMatch.Groups.Count < 5)
                    continue;

                string gameMSID = acheMatch.Groups[1].Value;
                string gameBoxSrc = acheMatch.Groups[2].Value;
                string gameName = acheMatch.Groups[3].Value;
                int gameAchievementsCount = int.Parse(acheMatch.Groups[5].Value);
                int playerAchievementCount = int.Parse(acheMatch.Groups[4].Value);
                consoleWriteLine("--Game : " + gameName + " " + i.ToString() + "/" + matchCol.Count.ToString());

                //check if the game exists in base
                int iGameId = DBInterface.getGameExists(gameMSID);
                if (iGameId == -1)
                {
                    consoleWriteLine("--New Game");

                    //create the game in database
                    iGameId = DBInterface.getNextValidIdForGame();
                    DBInterface.addNewGame(iGameId, gameName, gameBoxSrc, gameMSID, gameAchievementsCount);

                    //create directory
                    DBInterface.ftpCreateDirectory(Constant.FTP_ACHE_IMG + "/" + iGameId.ToString());

                    //extract achievements data
                    JSONData achievements = scanAchievements(int.Parse(gameMSID), player);

                    //for each achievements
                    foreach (JSONAchievement ache in achievements._achievements)
                    {

                        int acheID = DBInterface.getNextValidIdForAchievement();
                        //add achievement to the game
                        if (ache.IsHidden)
                        {
                            DBInterface.addAchievementToGame(acheID, "Succès secret", "Ce succès est secret. Vous devez le déverrouiller pour en savoir plus.", main.UrlHiddenAchievement, ache.Id, iGameId);
                            consoleWriteLine("---New achievement : Succès secret");
                        }
                        else
                        {
                            DBInterface.addAchievementToGame(acheID, ache.Name, ache.Description, ache.TileUrl, ache.Id, iGameId);
                            consoleWriteLine("---New achievement : " + ache.Name);
                        }

                        //achievements locked
                        if (!ache.Unlocked)
                            continue;

                        //add unlocked achievements to the player
                        DBInterface.addUnlockedAchievement(player._iID, acheID, ache.Score, ache.EarnedOn);
                        player._iFloatingGamerscore += ache.Score;
                        consoleWriteLine("---Unlocked achievement : " + ache.Name);
                    }
                    continue;
                }

                consoleWriteLine("--Old Game");
                //get the list of achievements
                JSONData acheList = scanAchievements(int.Parse(gameMSID), player);//Extract.extractAchievements(html);

                //HERE the game exists
                //check if the game has new achievements
                int currentAchievementsCount = DBInterface.getGameAchievementsCount(iGameId);
                if (gameAchievementsCount > currentAchievementsCount)
                {
                    //new achievements =>   update achievements list
                    consoleWriteLine("---New achievements to the game");
                    updateAchievementsList(acheList, iGameId);
                    DBInterface.updateGameAchievementsCount(iGameId, gameAchievementsCount);
                }

                //check if the player unlocked new achievements
                int iAchievementsUnlockedCount = DBInterface.getAchievementsUnlockedCount(player._iID, iGameId);
                if (iAchievementsUnlockedCount != playerAchievementCount)
                {
                    //unlock achievements
                    consoleWriteLine("---New achievements unlocked");
                    unlockAchievements(acheList, iGameId, player);
                }
            }

            //update gamerscore
            DBInterface.updatePlayerGamerscore(player._iID, currentGamerscore);

            return;
        }

        private bool scanIncPlayer(PlayerInfo player)
        {
            //call the activity page
            _browser.getWebPageAchievement("about:blank");
            string url = _urlCompareActivity + player._strGamertag.Replace(" ", "%20");
            string html = _browser.getWebPageActivity(url, player._strGamertag);
            if (html == "ERROR")
            {
                consoleWriteLine("Scan stoppé");
                return false;
            }

            //get the gamerscore
            //gamerscore => resMatch.Groups[1].Value
            string pattern = "<LI class=header>.*?<DIV class=stat>([0-9]*)?</DIV>";
            Match resMatch = Regex.Match(html, pattern, RegexOptions.Singleline);

            while (resMatch.Groups.Count != 2)
            {
                html = _browser.getWebPageActivity(url, player._strGamertag);
                resMatch = Regex.Match(html, pattern, RegexOptions.Singleline);
            }

            //update gamer picture
            if (player._iScanImage == 1)
            {
                consoleWriteLine("-Update Gamer Picture");
                updateGamerPicture(html, player);
            }

            //check if gamerscore changed
            int currentGamerscore = int.Parse(resMatch.Groups[1].Value);
            consoleWriteLine("-XBOXLIVE.com Gamerscore=" + currentGamerscore.ToString());
            consoleWriteLine("-ETERNAL-APOCALYPSE.fr Gamerscore=" + player._iGamerscore.ToString());
            if (currentGamerscore == player._iGamerscore)
                return true;

            //update gamerscore
            player._iFloatingGamerscore = player._iGamerscore;

            //extract games
            pattern = "<LI class=\"game.*?</LI>";
            MatchCollection matchCol = Regex.Matches(html, pattern, RegexOptions.Singleline);

            //for each game
            for (int i = 0; i < matchCol.Count; ++i)
            {
                Match m = matchCol[i];
                if (player._iFloatingGamerscore >= currentGamerscore)
                {
                    //update gamerscore
                    DBInterface.updatePlayerGamerscore(player._iID, currentGamerscore);
                    return true;
                }

                //extract game info
                //1=>msid
                //2=>box
                //3=>name
                //4=>achievements unlocked count
                //5=>achievements count
                pattern = "<LI class=\"game ([0-9]*)?\".*?src=\"(.*)?\"></A>.*?<A class=gameTitle.*?>(.*)?</A></H3>.*?<DIV class=achievement><SPAN>([0-9]*)</SPAN>/<SPAN>([0-9]*)</SPAN>";
                Match acheMatch = Regex.Match(m.Groups[0].Value, pattern, RegexOptions.Singleline);

                //no data then no achievement so no scan
                if (acheMatch.Groups.Count < 5)
                    continue;

                string gameMSID = acheMatch.Groups[1].Value;
                string gameBoxSrc = acheMatch.Groups[2].Value;
                string gameName = acheMatch.Groups[3].Value;
                int gameAchievementsCount = int.Parse(acheMatch.Groups[5].Value);
                int playerAchievementCount = int.Parse(acheMatch.Groups[4].Value);
                consoleWriteLine("--Game : " + gameName + " " + i.ToString() + "/" + matchCol.Count.ToString());

                //check if the game exists in base
                int iGameId = DBInterface.getGameExists(gameMSID);
                if (iGameId == -1)
                {
                    consoleWriteLine("--New Game");

                    //create the game in database
                    iGameId = DBInterface.getNextValidIdForGame();
                    DBInterface.addNewGame(iGameId, gameName, gameBoxSrc, gameMSID, gameAchievementsCount);

                    //create directory
                    DBInterface.ftpCreateDirectory(Constant.FTP_ACHE_IMG + "/" + iGameId.ToString());

                    //extract achievements data
                    JSONData achievements = scanAchievements(int.Parse(gameMSID), player);

                    //for each achievements
                    foreach (JSONAchievement ache in achievements._achievements)
                    {
                        
                        int acheID = DBInterface.getNextValidIdForAchievement();
                        //add achievement to the game
                        if (ache.IsHidden)
                        {
                            DBInterface.addAchievementToGame(acheID, "Succès secret", "Ce succès est secret. Vous devez le déverrouiller pour en savoir plus.", main.UrlHiddenAchievement, ache.Id, iGameId);
                            consoleWriteLine("---New achievement : Succès secret");
                        }
                        else
                        {
                            DBInterface.addAchievementToGame(acheID, ache.Name, ache.Description, ache.TileUrl, ache.Id, iGameId);
                            consoleWriteLine("---New achievement : " + ache.Name);
                        }

                        //achievements locked
                        if (!ache.Unlocked)
                            continue;

                        //add unlocked achievements to the player
                        DBInterface.addUnlockedAchievement(player._iID, acheID, ache.Score, ache.EarnedOn);
                        player._iFloatingGamerscore += ache.Score;
                        consoleWriteLine("---Unlocked achievement : " + ache.Name);
                    }
                    continue;
                }

                consoleWriteLine("--Old Game");
                //get the list of achievements
                JSONData acheList = scanAchievements(int.Parse(gameMSID), player);//Extract.extractAchievements(html);
                
                //HERE the game exists
                //check if the game has new achievements
                int currentAchievementsCount = DBInterface.getGameAchievementsCount(iGameId);
                if (gameAchievementsCount > currentAchievementsCount)
                {
                    //new achievements =>   update achievements list
                    consoleWriteLine("---New achievements to the game"); 
                    updateAchievementsList(acheList, iGameId);
                    DBInterface.updateGameAchievementsCount(iGameId, gameAchievementsCount);
                }

                //check if the player unlocked new achievements
                int iAchievementsUnlockedCount = DBInterface.getAchievementsUnlockedCount(player._iID, iGameId);
                if (iAchievementsUnlockedCount != playerAchievementCount)
                {
                    //unlock achievements
                    consoleWriteLine("---New achievements unlocked");
                    unlockAchievements(acheList, iGameId, player);
                }
            }

            //update gamerscore
            DBInterface.updatePlayerGamerscore(player._iID, currentGamerscore);

            return true;
        }

        private void updateGamerPicture(string html, PlayerInfo player)
        {
            //extract the picture 
            string pattern = "<IMG class=gamerpic.*?src=(.*)? </A>";
            Match resMatch = Regex.Match(html, pattern);

            //pattern = @"(.*)?>\s</A>";
            //resMatch = Regex.Match(resMatch.Groups[1].Value, pattern, RegexOptions.Singleline);

            //picture not found
            if (resMatch.Groups.Count != 2)
                return;

            string src = resMatch.Groups[1].Value.Substring(1, resMatch.Groups[1].Value.Length - 3);
            //save the picture
            bool res = DBInterface.getImg(src, Constant.FTP_PLAYER_IMG + "/" + player._iID.ToString() + ".jpg");
            if (res)
            {
                DBInterface.resetPlayerScanImage(player._iID);
                consoleWriteLine("-Gamer Picture saved");
            }
            else
                consoleWriteLine("-Gamer Picture ERROR");
        }

        private void unlockAchievements(JSONData json, int gameId, PlayerInfo player)
        {
            //for each achievements
            foreach (JSONAchievement ache in json._achievements)
            {
                //achievement locked
                if (!ache.Unlocked)
                    continue;

                //check if the achievement is unlocked
                bool res = DBInterface.getPlayerUnlockedAchievement(player._iID, gameId, ache.Id);
                if (res)
                    continue;

                //unlock achievement
                int acheID = DBInterface.getAchievementId(gameId, ache.Id);
                DBInterface.addUnlockedAchievement(player._iID, acheID, ache.Score, ache.EarnedOn);
                player._iFloatingGamerscore += ache.Score;
                consoleWriteLine("---Unlocked achievement : " + ache.Name);
            }
        }

        private void updateAchievementsList(JSONData json, int gameId)
        {
            //for each achievement
            foreach (JSONAchievement ache in json._achievements)
            {
                //check if the achievement exists
                bool res = DBInterface.getAchievementExists(gameId, ache.Id);
                if (res)
                    continue;

                //it doesn't exist so we create it
                int acheID = DBInterface.getNextValidIdForAchievement();
                if (ache.IsHidden)
                {
                    DBInterface.addAchievementToGame(acheID, "Succès secret", "Ce succès est secret. Vous devez le déverrouiller pour en savoir plus.", main.UrlHiddenAchievement, ache.Id, gameId);
                    consoleWriteLine("---New achievement : Succès secret");
                }
                else
                {
                    DBInterface.addAchievementToGame(acheID, ache.Name, ache.Description, ache.TileUrl, ache.Id, gameId);
                    consoleWriteLine("---New achievement : " + ache.Name);
                }
            }
        }

        private JSONData scanAchievements(int gameMSID, PlayerInfo player)
        {
            //get the achievements page
            _browser.getWebPageAchievement("about:blank");
            string url = "https://live.xbox.com/fr-FR/Activity/Details?titleId=" + gameMSID + "&compareTo=" + player._strGamertag.Replace(" ", "%20");
            string html = _browser.getWebPageAchievement(url);

            //extract the json in resultRegex.Groups[1].Value
            string pattern = @"broker\.publish\(routes\.activity\.details\.load, (.*)?\);";
            Match resultRegex = Regex.Match(html, pattern);
            while (resultRegex.Groups.Count < 2)
            {
                html = _browser.getWebPageAchievement(url);
                resultRegex = Regex.Match(html, pattern);
            }

            JSONData ret = Extract.extractAchievements(html);
            return ret;
        }
        #endregion

    }
}
